﻿using Nop.Core.Data;
using Nop.Core.Domain.Orders;
using Nop.Services.Common;
using Nop.Web.Services.Domain;
using Nop.Services.Logging;
using Nop.Web.Services.Utils;
using System;
using Nop.Web.Services.Logging;
using System.IO;
using Nop.Web.Services.Data;
using System.Linq;
using Nop.Services.Directory;
using System.Collections.Generic;
using System.Text;
using Nop.Data.Extensions;
using UserCenter.Console.Sdk.Models;
using Nop.Core.Configuration;
using Nop.Web.Services.Models;
using Microsoft.EntityFrameworkCore;
using Nop.Core.Domain.Customers;
using Nop.Web.Services.Mob;
using PayCenter.Sdk.Models;
using Newtonsoft.Json;
using Nop.Services.Orders;
using Nop.Core;
using Nop.Core.Infrastructure;


namespace Nop.Web.Services.Factories
{
    public class HUCFactory
    {
        const string TAG = "订单统计：";
        static readonly IWalletRule[] walletRules = new IWalletRule[] { new ChildRule(), new CurrentRule() };


        private readonly ICurrencyService _currencyService;
        private readonly ServicesObjectContext _servicesObjectContext;
        private readonly IRepository<CustomerOrderCount> _customerOrderCountRepository;
        private readonly IRepository<HPayWalletQueued> _HPayWalletQueuedRepository;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly SafeLogger _logger;
        private readonly HUCSdkHelper _hUCSdkHelper;
        private readonly NopConfig _nopConfig;

        private readonly MobPushHelper _pushHelper;
        private readonly HPaySdkHelper _paySdkHelper;


        static List<long> transferedIdList = new List<long>();
        static List<long> orderIdList = new List<long>();

        public HUCFactory(
                  ICurrencyService currencyService,
                  ServicesObjectContext servicesObjectContext,
                  IRepository<CustomerOrderCount> customerOrderCountRepository,
                  IRepository<HPayWalletQueued> HPayWalletQueuedRepository,
                  IGenericAttributeService genericAttributeService,
                  SafeLogger logger,
                  HUCSdkHelper hUCSdkHelper,
                  NopConfig nopConfig,
                  MobPushHelper mobPushHelper,
                  HPaySdkHelper paySdkHelper
              )
        {
            _currencyService = currencyService;
            _servicesObjectContext = servicesObjectContext;
            _customerOrderCountRepository = customerOrderCountRepository;
            _HPayWalletQueuedRepository = HPayWalletQueuedRepository;
            _genericAttributeService = genericAttributeService;
            _logger = logger;
            _hUCSdkHelper = hUCSdkHelper;
            _nopConfig = nopConfig;


            _pushHelper = mobPushHelper;
            _paySdkHelper = paySdkHelper;

            _paySdkHelper.Get().OnWalletTrasfer += Sdk_OnWalletTrasfer;
            _paySdkHelper.Get().OnPayStatus += Sdk_OnPayStatus;


        }
        private bool Sdk_OnPayStatus(PayNotifyMessage message)
        {
            if (orderIdList.Contains(message.OrderId))
            {
                return true;
            }
            orderIdList.Add(message.OrderId);
            var orderService = EngineContext.Current.Resolve<IOrderService>();
            var orderProcessingService = EngineContext.Current.Resolve<IOrderProcessingService>();

            string json = JsonConvert.SerializeObject(message);

            _logger.Information("收到支付通知:" + json);
            if (Guid.TryParse(message.OutTradeNo, out Guid orderId))
            {
                var order = orderService.GetOrderByGuid(orderId);
                if (order != null)
                {
                    //order note
                    //order.OrderNotes.Add(new OrderNote
                    //{
                    //    Note = $"支付通知\n{json}",
                    //    DisplayToCustomer = false,
                    //    CreatedOnUtc = DateTime.UtcNow
                    //});
                    orderService.UpdateOrder(order);
                    switch (message.Status)
                    {
                        case PayNotifyMessage.PayStatus.PaySuccess:
                            {
                                int orderTotal = Convert.ToInt32(order.OrderTotal) * 100;

                                if (message.TotalAmount == orderTotal)
                                {
                                    if (orderProcessingService.CanMarkOrderAsPaid(order))
                                    {
                                        order.OrderNotes.Add(new OrderNote
                                        {
                                            Note = $"订单支付成功,支付订单ID:" + message.OrderId,
                                            DisplayToCustomer = true,
                                            CreatedOnUtc = DateTime.UtcNow
                                        });
                                        orderProcessingService.MarkOrderAsPaid(order);

                                    }
                                }
                                else
                                {
                                    string errorStr = order.OrderGuid + "订单金额和支付网关返回金额不对应";
                                    _logger.Error(errorStr);
                                    //order note
                                    order.OrderNotes.Add(new OrderNote
                                    {
                                        Note = errorStr,
                                        DisplayToCustomer = false,
                                        CreatedOnUtc = DateTime.UtcNow
                                    });
                                    orderService.UpdateOrder(order);
                                }


                            }
                            break;
                        case PayNotifyMessage.PayStatus.Closed:
                            if (orderProcessingService.CanCancelOrder(order))
                            {
                                orderProcessingService.CancelOrder(order, true);
                            }
                            break;
                    }


                }
                else
                {
                    _logger.Error("HPay__OnPayStatus 订单编号没有发现", new NopException(json));
                }
            }
            else
            {
                _logger.Error("HPay__OnPayStatus 订单编号不是GUID格式", new NopException(json));
            }
            //  message.TotalAmount

            //  _logger.Error
            return true;
        }

        private bool Sdk_OnWalletTrasfer(PayCenter.Sdk.Models.WalletTrasferNotifyMessage arg)
        {
            try
            {
                if (transferedIdList.Contains(arg.TrasferId))
                {
                    return true;
                }
                transferedIdList.Add(arg.TrasferId);
                string mobAlia = string.Format("huc{0}", arg.DistMemberId);
                _pushHelper.Send(mobAlia, "用户转账", $"收到转账{(arg.Amount / 100).ToString("0.00")}", new
                {
                    memberId = arg.DistMemberId,
                    type = "walletChanged",
                    ext = new
                    {
                        tradeNo = arg.TrasferId,
                        walletType = arg.DistType,
                        amount = arg.Amount
                    }
                });
                mobAlia = string.Format("huc{0}", arg.SrcMemberId);
                _pushHelper.Send(mobAlia, "用户转账", $"转账{(arg.Amount / 100).ToString("0.00")}已成功", new
                {
                    memberId = arg.SrcMemberId,
                    type = "walletChanged",
                    ext = new
                    {
                        tradeNo = arg.TrasferId,
                        walletType = arg.SrcMemberId,
                        amount = arg.Amount
                    }
                });
            }
            catch (Exception ex)
            {


            }
            return true;
        }

        public void CalcOrderCount(Order order)
        {
            _logger.Information($"开始计算业绩，订单[{order.Id}]");
            var ucMemberId = _hUCSdkHelper.GetMemberId(order.Customer);
            if (ucMemberId < 1)
            {
                _logger.Warning($"{TAG}订单[{order.Id}]由于用户未映射到用户中心跳过计算");
            }
            else
            {
                try
                {
                    var parentsResult = _hUCSdkHelper.Get().GetParents(ucMemberId).Result;
                    if (parentsResult.Success)
                    {
                        var parents = parentsResult.Parents;
                        if (parents.Count > 0)
                        {
                            var parent = parents.Where(p => p.MemberId == ucMemberId).Take(1).SingleOrDefault();
                            if (parent != null && parent.Layer == 0)
                            {
                                parents = parents.OrderBy(p => p.Layer).Where(p => p.Layer < _nopConfig.OrderCountLayer - 1).ToList();//只计算OrderCountLayer级
                                var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
                                var parentOrderCounts = parents.Select(p => new CustomerOrderCount() { HUC_MemberId = p.ParentId, ChildOrderAmount = orderTotalInCustomerCurrency, LastChildOrderId = order.Id });
                                var customerOrderCount = new CustomerOrderCount()
                                {
                                    CustomerId = order.CustomerId,
                                    HUC_MemberId = ucMemberId,
                                    OrderAmount = orderTotalInCustomerCurrency,
                                    LastOrderId = order.Id
                                };
                                bool isUpdated = WriteOrderCount(customerOrderCount, parentOrderCounts);
                                if (isUpdated)
                                {
                                    CalcWalletQueue(order, parents, ucMemberId);
                                }
                            }
                            else
                            {
                                _logger.Error($"{TAG}订单[{order.Id}]上级数据不匹配,UC_MemberId:{ucMemberId}");
                            }
                        }
                        else
                        {
                            var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
                            var customerOrderCount = new CustomerOrderCount()
                            {
                                CustomerId = order.CustomerId,
                                HUC_MemberId = ucMemberId,
                                OrderAmount = orderTotalInCustomerCurrency,
                                LastOrderId = order.Id
                            };
                            bool isUpdated = WriteOrderCount(customerOrderCount, new List<CustomerOrderCount>());
                            if (isUpdated)
                            {
                                CalcWalletQueue(order, parents, ucMemberId);
                            }
                        }
                    }
                    else
                    {
                        _logger.Warning($"{TAG}订单[{order.Id}]获取上级列表错误,下次计算");
                        WriteNextTimeOrder(order);
                    }

                }
                catch (Exception ex)
                {
                    _logger.Error($"{TAG}订单计算发生错误[{order.Id}]", ex);

                }
            }
            _logger.Information($"完成计算业绩，订单[{order.Id}]");
        }

        public void CalcWalletQueue(Order order, List<ParentLayer> parents, long orderMemberId)
        {
            _logger.Information($"开始计算订单收益，订单[{order.Id}]");
            if (parents.Count > 0)
            {
                var hucMemberIdList = parents.Select(c => c.ParentId).ToList();
                hucMemberIdList.Add(orderMemberId);
                var existsCountList = _customerOrderCountRepository.TableNoTracking.Where(r => hucMemberIdList.Contains(r.HUC_MemberId)).ToList(); 
                if (existsCountList.Count != hucMemberIdList.Count)
                {
                    _logger.Error($"{TAG}订单[{order.Id}]上级数据不匹配,写入钱包时用户中心上级数量[{hucMemberIdList.Count}]不等于已计算上级数量[{existsCountList.Count}]");
                }
                else
                {
                    var ruleInfoList = hucMemberIdList.Select(p => new WalletRuleInfo
                    {
                        MemberId = p,
                        OrderCount = existsCountList.Where(r => r.HUC_MemberId == p).Take(1).SingleOrDefault()
                    }).Where(p => p.OrderCount != null);
                    if (ruleInfoList.Count() != hucMemberIdList.Count)
                    {
                        _logger.Error($"{TAG}订单[{order.Id}]上级数据不匹配,写入钱包时用户中心上级数量[{hucMemberIdList.Count}]不等于已合并订单统计数量[{ruleInfoList.Count()}]");
                    }
                    else
                    {
                        Dictionary<string, List<WriteWalletQueueParameter.MemberPercent>> ruleMemberPercentMap = new Dictionary<string, List<WriteWalletQueueParameter.MemberPercent>>();
                        foreach (var rule in walletRules)
                        {
                            var memberPercents = rule.Calc(ruleInfoList, orderMemberId, _nopConfig);
                            if (memberPercents != null && memberPercents.Count > 0)
                            {
                                ruleMemberPercentMap[rule.Name] = memberPercents;
                            }
                        }
                        foreach (var rule in walletRules)
                        {
                            rule.Combin(ruleMemberPercentMap);
                        }

                        var ruleMemberPercents = new List<WriteWalletQueueParameter.RuleMemberPercent>();
                        foreach (var kv in ruleMemberPercentMap)
                        {
                            if (kv.Value != null && kv.Value.Count > 0)
                            {
                                var rule = walletRules.Where(s => s.Name == kv.Key).SingleOrDefault();
                                ruleMemberPercents.Add(new WriteWalletQueueParameter.RuleMemberPercent()
                                {
                                    MemberPercents = kv.Value,
                                    RuleName = rule == null ? "未知规则" : rule.FriedlyName
                                });
                            }
                        }

                        if (ruleMemberPercents.Count > 0)
                        {
                            var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
                            bool isWrited = WriteWalletQueue(new WriteWalletQueueParameter()
                            {
                                Amount = orderTotalInCustomerCurrency,
                                DescribeTemplate = "邀请人收益,订单[{0}]{1}分到红包{2}%",
                                WalletType = _nopConfig.DefaultWalletType,
                                EntityId = order.Id,
                                SourceType = HPayWalletQueued._SourceType.Order,
                                RuleMemberPercents = ruleMemberPercents
                            });
                            if (!isWrited)
                            {
                                _logger.Warning($"{TAG}订单[{order.Id}]写入钱包更新队列出错,下次计算");
                                WriteNextTimeWallet(order);
                            }
                        }

                    }
                }
            }
            else
            {
                var memberPercents = new List<WriteWalletQueueParameter.MemberPercent>();
                memberPercents.Add(new WriteWalletQueueParameter.MemberPercent()
                {
                    CustomerId = order.CustomerId,
                    HUC_MemberId = orderMemberId,
                    Percent = 0.01f
                });
                var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
                bool isWrited = WriteWalletQueue(new WriteWalletQueueParameter()
                {
                    Amount = orderTotalInCustomerCurrency,
                    DescribeTemplate = "邀请人收益,订单[{0}]{1}分到红包{2}%",
                    WalletType = _nopConfig.DefaultWalletType,
                    EntityId = order.Id,
                    SourceType = HPayWalletQueued._SourceType.Order,
                    RuleMemberPercents = new List<WriteWalletQueueParameter.RuleMemberPercent> {
                    new WriteWalletQueueParameter.RuleMemberPercent(){
                     MemberPercents=memberPercents, RuleName="固定分润"
                    }
                    }
                });
                if (!isWrited)
                {
                    _logger.Warning($"{TAG}订单[{order.Id}]写入钱包更新队列出错,下次计算");
                    WriteNextTimeWallet(order);
                }
            }


            _logger.Information($"完成计算订单收益，订单[{order.Id}]");
        }


        private bool WriteWalletQueue(WriteWalletQueueParameter parameter)
        {
            try
            {
                var writeList = parameter.RuleMemberPercents.SelectMany(rmp => rmp.MemberPercents.Select(mp => new HPayWalletQueued()
                {
                    CreatedOnUtc = DateTime.UtcNow,
                    Amount = Convert.ToDecimal(mp.Percent) * parameter.Amount,
                    CustomerId = mp.CustomerId,
                    Describe = string.Format(parameter.DescribeTemplate, parameter.EntityId, rmp.RuleName, Convert.ToInt32(mp.Percent * 100)),
                    EntityId = parameter.EntityId,
                    HUC_MemberId = mp.HUC_MemberId,
                    LastActionTimeOnUtc = DateTime.UtcNow,
                    Passed = false,
                    PassTimeOnUtc = DateTime.UtcNow,
                    Runing = false,
                    SourceType = (byte)parameter.SourceType,
                    TryCount = 0,
                    WalletType = parameter.WalletType
                }));
                _HPayWalletQueuedRepository.Insert(writeList);
                return true;
            }
            catch (Exception ex)
            {
                _logger.Error($"{TAG}{parameter.SourceType}[{parameter.EntityId}]写入钱包队列出错", ex);
                return false;

            }
        }

        public bool WriteOrderCount(CustomerOrderCount customerOrderCount, IEnumerable<CustomerOrderCount> parentOrderCounts)
        {
            try
            {
                var hucMemberIdList = parentOrderCounts.Select(c => c.HUC_MemberId).ToList();
                hucMemberIdList.Add(customerOrderCount.HUC_MemberId);
                var existsCountList = _customerOrderCountRepository.TableNoTracking.Where(r => hucMemberIdList.Contains(r.HUC_MemberId)).ToList();

                StringBuilder strSql = new StringBuilder();
                if (existsCountList.Where(e => e.HUC_MemberId == customerOrderCount.HUC_MemberId).Any())
                {
                    strSql.AppendLine($"update CustomerOrderCount set CustomerId={customerOrderCount.CustomerId},OrderAmount=OrderAmount+{customerOrderCount.OrderAmount},LastOrderId={customerOrderCount.LastOrderId} where HUC_MemberId={customerOrderCount.HUC_MemberId};");
                }
                else
                {
                    strSql.Append("INSERT INTO CustomerOrderCount (CustomerId, HUC_MemberId, OrderAmount, ChildOrderAmount, CountTimeOnUtc , LastOrderId, LastChildOrderId) VALUES ");
                    strSql.AppendLine($"({customerOrderCount.CustomerId},{customerOrderCount.HUC_MemberId},{customerOrderCount.OrderAmount},0,'{DateTime.UtcNow}',{customerOrderCount.LastOrderId},0);");
                }
                foreach (var parentOrderCount in parentOrderCounts)
                {
                    if (existsCountList.Where(e => e.HUC_MemberId == parentOrderCount.HUC_MemberId).Any())
                    {
                        strSql.AppendLine($"update CustomerOrderCount set  ChildOrderAmount=ChildOrderAmount+{parentOrderCount.ChildOrderAmount},LastChildOrderId={parentOrderCount.LastChildOrderId} where HUC_MemberId={parentOrderCount.HUC_MemberId};");
                    }
                    else
                    {
                        strSql.Append("INSERT INTO CustomerOrderCount (CustomerId, HUC_MemberId, OrderAmount, ChildOrderAmount, CountTimeOnUtc , LastOrderId, LastChildOrderId) VALUES ");
                        strSql.AppendLine($"(0,{parentOrderCount.HUC_MemberId},0,{parentOrderCount.ChildOrderAmount},'{DateTime.UtcNow}',0,{parentOrderCount.LastChildOrderId});");
                    }
                }
                _servicesObjectContext.ExecuteSqlScript(strSql.ToString());
                return true;

            }
            catch (Exception ex)
            {
                _logger.Error($"{TAG}订单[{customerOrderCount.LastOrderId}]写入订单计算结果出错", ex);
                return false;
            }
        }

        private void WriteNextTimeOrder(Order order)
        {
            string path = ".NextTimeOrder";
            try
            {
                if (!File.Exists(path))
                {
                    File.Create(path).Dispose();
                }
                File.AppendAllLines(path, new string[] { order.Id.ToString() });
            }
            catch (Exception ex)
            {
                _logger.Error($"{TAG}订单[{order.Id}]下次订单统计标记文件写入错误", ex);
            }
        }


        private void WriteNextTimeWallet(Order order)
        {
            string path = ".NextTimeWallet";
            try
            {
                if (!File.Exists(path))
                {
                    File.Create(path).Dispose();
                }
                File.AppendAllLines(path, new string[] { order.Id.ToString() });
            }
            catch (Exception ex)
            {
                _logger.Error($"{TAG}订单[{order.Id}]下次钱包更新计算标记文件写入错误", ex);
            }
        }


        public bool LockWalletQueue(int queueId)
        {
            return _servicesObjectContext.ExecuteSqlCommand($"UPDATE HPayWalletQueued SET Runing=1 WHERE Id = {queueId} AND Runing=0 AND Passed=0 ") > 0;
        }


        public bool UnLockWalletQueueOfPass(int queueId)
        {
            var result = _servicesObjectContext.ExecuteSqlCommand($"UPDATE HPayWalletQueued SET Runing=0,Passed=1,PassTimeOnUtc=GETUTCDATE(),LastActionTimeOnUtc=GETUTCDATE() WHERE Id ={queueId} AND Runing=1 AND Passed=0") > 0;
            if (!result)
            {
                _logger.Warning($"{TAG}钱包队列[{queueId}]执行成功解除锁定失败");
            }
            return result;
        }

        public bool UnLockWalletQueueOfError(int queueId)
        {
            var result = _servicesObjectContext.ExecuteSqlCommand($"UPDATE HPayWalletQueued SET Runing=0, TryCount=TryCount+1,LastActionTimeOnUtc=GETUTCDATE() WHERE Id ={queueId} AND Runing=1 AND Passed=0") > 0;
            if (!result)
            {
                _logger.Warning($"{TAG}钱包队列[{queueId}]执行失败解除锁定失败");
            }
            return result;
        }


        public List<HPayWalletQueued> UnPassWalletQueueList()
        {
            return _HPayWalletQueuedRepository.Table.Where(r => !r.Passed && !r.Runing).OrderBy(r => r.Id).Take(20).ToList(); ;
        }


        public (List<CustomerOrderCountModel> list, bool result) ChildrenOrderCount(Customer customer, bool includeSelf = false)
        {
            var ucMemberId = _hUCSdkHelper.GetMemberId(customer);
            var list = new List<CustomerOrderCountModel>();
            if (ucMemberId < 1)
            {
                return (list, false);
            }
            var childrenResult = _hUCSdkHelper.Get().GetChildren(ucMemberId).Result;
            if (!childrenResult.Success)
            {
                return (list, false);
            }
            var children = childrenResult.Children;
            if (includeSelf)
            {
                children.Add(ucMemberId);
            }
            var childCountResult = _hUCSdkHelper.Get().GetChildCount(children).Result;
            if (!childCountResult.Success)
            {
                return (list, false);
            }

            var orderCount = _customerOrderCountRepository.TableNoTracking.Where(s => children.Contains(s.HUC_MemberId)).Select(x => x).ToList();
            list = children.Select(memberId =>
             {
                 var childCountInfo = childCountResult.CountList.Where(m => m.MemberId == memberId).SingleOrDefault();
                 var orderCountInfo = orderCount.Where(m => m.HUC_MemberId == memberId).SingleOrDefault();
                 return new CustomerOrderCountModel()
                 {
                     ChildOrderAmount = orderCountInfo != null ? orderCountInfo.ChildOrderAmount : 0,
                     CountTimeOnUtc = orderCountInfo != null ? orderCountInfo.CountTimeOnUtc : DateTime.Now,
                     CustomerId = orderCountInfo != null ? orderCountInfo.CustomerId : 0,
                     HUC_MemberId = memberId,
                     LastChildOrderId = orderCountInfo != null ? orderCountInfo.LastChildOrderId : 0,
                     LastOrderId = orderCountInfo != null ? orderCountInfo.LastOrderId : 0,
                     OrderAmount = orderCountInfo != null ? orderCountInfo.OrderAmount : 0,
                     ChildCount = childCountInfo != null ? childCountInfo.ChildCount : 0,
                     GroupCount = childCountInfo != null ? childCountInfo.GroupCount : 0
                 };
             }).ToList();
            if (!list.Where(s => s.HUC_MemberId == ucMemberId).Any())
            {
                list.Add(new CustomerOrderCountModel()
                {
                    HUC_MemberId = ucMemberId,
                    CustomerId = customer.Id
                });
            }
            else
            {
                if (list.Last().HUC_MemberId != ucMemberId)
                {
                    var current = list.Where(s => s.HUC_MemberId == ucMemberId).FirstOrDefault();
                    list.Remove(current);
                    list.Add(current);
                }
            }
            return (list, true);

        }
    }
}
